from algo.data_structure.doubly_linked_base import _DoublyLinkedBase


class DoublyLinkedDeque(_DoublyLinkedBase):
    def __str__(self):
        elements = []
        walk = self._header._next
        while (walk != self._tailer):
            elements.append(walk._element)
            walk = walk._next
        return str(elements)

    def first(self):
        """Return the element at the front of the deque"""
        if self.is_empty():
            raise Exception("deque is empty")
        return self._header._next._element

    def last(self):
        """Return the element at the tail of the deque"""
        if self.is_empty():
            raise Exception("deque is empty")
        return self._tailer._prev._element

    def add_first(self, value):
        """
        Add the value to front of the queue
        :param value:
        :return:
        """
        self._insert_between(value, self._header, self._header._next)

    def add_last(self, value):
        """
        Add the value to tail of the deque
        :param value:
        :return:
        """
        self._insert_between(value, self._tailer._prev, self._tailer)

    def del_first(self):
        """
        Delete and return the element from the front of the deque
        :return:
        """
        if self.is_empty():
            raise Exception("deque is empty")
        return self._delete_node(self._header._next)

    def del_last(self):
        """
        Delete and return the element from the last of the deque
        :return:
        """
        if self.is_empty():
            raise Exception("deque is empty")
        return self._delete_node(self._tailer._prev)


